import { IStageAssetsInfo, Stage } from "babylon-lib/base/stage";
import { Scene } from "@babylonjs/core/scene";
import { Camera } from "@babylonjs/core/Cameras/camera";
import { Light } from "@babylonjs/core/Lights/light";
import { RawAssetsUrl } from "../raw-assets-info";
import {
    AbstractAssetTaskInfo,
} from "babylon-lib/loader/asset-task-info";
import { ILoose } from "babylon-lib/tool/utils";
import { GLTFMeshTaskInfo } from "babylon-lib/loader/asset-task-info";
import { UniversalCamera } from "@babylonjs/core/Cameras/universalCamera";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { CameraInputPreset } from 'babylon-lib/preset/camera-input-preset';
import { Helper } from "babylon-lib/tool/helper";
import { Mesh } from "@babylonjs/core/Meshes/mesh";
import { AnimationGroup, Bone, Color3, Matrix, PBRMaterial, Quaternion, Texture, TransformNode } from "@babylonjs/core";
import { AssetsLoad } from "babylon-lib/index";
import { Random } from 'babylon-lib/tool/random';


class DefaultStage extends Stage {

    protected assetTaskInfos(): ILoose<AbstractAssetTaskInfo> | Array<IStageAssetsInfo> {
        return {
            ...[
                new GLTFMeshTaskInfo(RawAssetsUrl.gallery),
                new GLTFMeshTaskInfo(RawAssetsUrl.roleAnimate),
                new GLTFMeshTaskInfo(RawAssetsUrl.roleCloth),
                new GLTFMeshTaskInfo(RawAssetsUrl.roleCloth2),
            ]
        } as unknown as ILoose<AbstractAssetTaskInfo>;
    }

    protected initializeCameras(scenes: Array<Scene>): Array<Camera> {
        const camera = new UniversalCamera("UniversalCamera", new Vector3(0, 2, 5), scenes[0]);
        Helper.markGeneralCamera(camera);
        camera.attachControl(true);
        camera.speed = 0.2;
        camera.setTarget(new Vector3(0, 1, 0));
        camera.minZ = 0.1;
        camera.maxZ = 200;
        return [camera];
    }

    protected initializeLights(scenes: Array<Scene>, cameras: Array<Camera>): Array<Light> {
        const light = super.initializeLights(scenes, cameras)[0];
        light.intensity = 1;
        return [light];
    }

    protected async initializeComponents(scenes: Scene[], cameras: Camera[], lights: Light[]): Promise<void> {
        const loader = this.loader(scenes[0]);
        this.setGalleryMaterial(loader);
        this.setRoleAnimation(loader);
    }

    private setGalleryMaterial(loader: AssetsLoad): void {
        const model = loader.getMeshTaskInfo(RawAssetsUrl.gallery)!;

        /**
         * blender输出env图时x平移0.25，且flipY，快速
         */
        const envMesh = Helper.nodeInArray('envHdr', model.loadedMeshes!, undefined, true) as Mesh;
        const mat = envMesh.material as PBRMaterial;
        const texture = mat.albedoTexture as Texture;
        texture.coordinatesMode = Texture.EQUIRECTANGULAR_MODE;
        mat.albedoTexture = null;
        mat.albedoColor = new Color3(1,0.6,0);
        mat.metallic = 0.9;
        mat.roughness = 0.1;

        /**
         * 不需对env图输出做特殊处理，较慢
         */
        // const bfr = texture._buffer;
        // const b = new Blob([bfr as any], { type: 'image/png' });
        // const url = URL.createObjectURL(b);
        // const env3 = new EquiRectangularCubeTexture(url!, this.mainScene, 2048);
        // env3.level = 1.5;

        model.loadedMeshes!.forEach(mesh => {
            mesh.isPickable = false;
            const mat = mesh.material as PBRMaterial;
            if (mat) {
                mat.reflectionTexture = texture;
                mat.backFaceCulling = true;
            }
            if (mat && mat.emissiveTexture) {
                const lightMap = mat.emissiveTexture;
                mat.emissiveTexture = null;
                lightMap.coordinatesIndex = 1;
                lightMap.level = 2;
                mat.lightmapTexture = lightMap;
                mat.useLightmapAsShadowmap = true;

                mesh.freezeWorldMatrix();
                mesh.doNotSyncBoundingInfo = true;
            }
        });
    }

    private setRoleAnimation(loader: AssetsLoad): void {
        const model = loader.getMeshTaskInfo(RawAssetsUrl.roleAnimate)!;
        console.log(model)
        model.loadedAnimationGroups?.forEach(ag => {
            ag.stop();
        });

        const ag = Random.select<AnimationGroup>(...model.loadedAnimationGroups!);
        console.log(ag)
        ag.loopAnimation = true;
        setTimeout(() => {
            ag.play(true);
        }, 2000);

        const clothModel = loader.getMeshTaskInfo(RawAssetsUrl.roleCloth)!;
        console.log(clothModel)
        clothModel.loadedMeshes?.forEach(m => {
            m.alwaysSelectAsActiveMesh = true;
        });
        const clothModel2 = loader.getMeshTaskInfo(RawAssetsUrl.roleCloth2)!;
        console.log(clothModel2)
        clothModel2.loadedMeshes?.forEach(m => {
            m.alwaysSelectAsActiveMesh = true;
        });

        const clothS = clothModel.loadedSkeletons![0]!;
        const cloth2S = clothModel2.loadedSkeletons![0]!;
        const animS = model.loadedSkeletons![0]!;

        const getNode = (name: string): [Bone, TransformNode] | null => {
            for (let i = 0; i < animS.bones.length; i++) {
                const n = animS.bones[i].getTransformNode()!;
                const str = n.name.split(/_|:/);
                if (str.includes(name)) return [animS.bones[i], n];
            }
            return null;
        }

        const ofsP = new Vector3(2, 0, 0);
        setInterval(() => {
            clothS.bones.forEach(bone => {
                const cn = bone.getTransformNode()!;
                const an = getNode(cn.name);
                if (an) {
                    const tn = an![1];
                    cn.position.copyFrom(tn.position);
                    cn.rotationQuaternion!.copyFrom(tn.rotationQuaternion!);
                    cn.scaling.copyFrom(tn.scaling);
                }
            });
            cloth2S.bones.forEach(bone => {
                const cn = bone.getTransformNode()!;
                const an = getNode(cn.name);
                if (an) {
                    const tn = an![1];
                    cn.position.copyFrom(tn.position);
                    cn.rotationQuaternion!.copyFrom(tn.rotationQuaternion!);
                    cn.scaling.copyFrom(tn.scaling);
                }
            });
        }, 16);

    }

    protected initializeInput(scenes: Array<Scene>, cameras: Array<Camera>): void {
        new CameraInputPreset(this.mainScene);
    }

}


export { DefaultStage };